package com.jlf.starter.advice;

import com.jlf.core.error.Code;
import com.jlf.core.error.Errors;
import com.jlf.core.error.exception.*;
import com.jlf.core.output.ResultException;
import com.jlf.core.output.ResultWrapper;
import com.jlf.core.output.VoidResultWrapper;
import com.jlf.starter.advice.conf.AdviceProperties;
import com.jlf.starter.advice.entity.ExceptionCodeConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 统一异常处理
 *
 * @author wujr
 * 2023/7/13
 * @version 1.0
 * 【变更历史】
 * [wujr 2023/7/13 1.0 新增]
 */
@Slf4j
@RestControllerAdvice
public class ExceptionAdvice {
    static final List<String> RECORD_EXISTS = Arrays.asList("org.springframework.dao.DuplicateKeyException", "java.sql.SQLIntegrityConstraintViolationException");
    static final String RECORD_PG_EXIST_EXP = "org.postgresql.util.PSQLException";
    static final String RECORD_PG_EXIST_STATE = "23505";
    private static final ConcurrentHashMap<String, ResultWrapper<Void>> _errors = new ConcurrentHashMap<>();
    private final AdviceProperties adviceProperties;
    @Autowired(required = false)
    private ExceptionResultProcessor exceptionResultProcessor;

    public ExceptionAdvice(AdviceProperties adviceProperties){
        this.adviceProperties = adviceProperties;
        // HttpRequestMethodNotSupportedException：不支持的接口（方法）
        _errors.put(HttpRequestMethodNotSupportedException.class.getName(), ResultWrapper.error(adviceProperties.getUnsupportedApi()));
        // HttpMessageNotReadableException：无法读取需要的参数
        _errors.put(HttpMessageNotReadableException.class.getName(), ResultWrapper.error(adviceProperties.getInvalidParameter()));
    }
    @ExceptionHandler(value = MissingPathVariableException.class)
    public VoidResultWrapper pathVariableException(MissingPathVariableException e) {
        String msg = "缺少路径参数：" + e.getVariableName();
        return ResultWrapper.error(adviceProperties.getInvalidParameter(), msg);
    }
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResultWrapper<Void> argumentTypeHandler(MethodArgumentNotValidException e){
        List<FieldError> errors = e.getBindingResult().getFieldErrors();
        String msg = "";
        if (!errors.isEmpty()){
            StringBuilder sb = new StringBuilder();
            String split = ", ";
            for (FieldError error : errors){
                sb.append(split).append(error.getDefaultMessage());
            }
            msg = sb.toString();
            if (msg.startsWith(split)){
                msg = msg.substring(split.length());
            }
        }
        return ResultWrapper.error(adviceProperties.getInvalidParameter(), msg);
    }
    @ExceptionHandler(value = MissingRequestHeaderException.class)
    public ResultWrapper<Void> missingHeaderHandler(MissingRequestHeaderException e){
        log.warn("请求头部缺少必要的参数：", e);
        String msg = "请求头部缺少必要的参数";
        return ResultWrapper.error(adviceProperties.getInvalidParameter(), msg);
    }
    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    public ResultWrapper<Void> argumentMismatchHandler(MethodArgumentTypeMismatchException e){
        String msg = e.getName() + ": 数据类型不匹配";
        return ResultWrapper.error(adviceProperties.getInvalidParameter(), msg);
    }
    @ExceptionHandler(value = BindException.class)
    public ResultWrapper<Void> argumentMissingHandle(BindException e){
        StringBuilder sb = new StringBuilder();
        BindingResult bindingResult = e.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        boolean first = true;
        for (FieldError fieldError : fieldErrors){
            if (first){
                first = false;
            }else{
                sb.append(", ");
            }
            sb.append(fieldError.getField())
                    .append(": ")
                    .append(fieldError.getDefaultMessage());
        }
        return ResultWrapper.error(adviceProperties.getInvalidParameter(), sb.toString());
    }
    @ExceptionHandler(value = NullReferenceException.class)
    public ResultWrapper<Void> nullReference(NullReferenceException e){
        String msg = e.getMessage();
        return ResultWrapper.error(Errors.NULL_REFERENCE, msg);
    }
    @ExceptionHandler(value = ReferenceMismatchException.class)
    public ResultWrapper<Void> referenceMismatch(ReferenceMismatchException e){
        String msg = e.getMessage();
        return ResultWrapper.error(Errors.REFERENCE_MISMATCH, msg);
    }
    @ExceptionHandler(value = ModifyObjectException.class)
    public ResultWrapper<Void> modifyObject(AddObjectException e){
        Throwable cause = e.getCause();
        if (cause instanceof ResultException){
            return ResultWrapper.error((ResultException)cause);
        }
        String msg = e.getMessage();
        return ResultWrapper.error(Errors.OBJECT_UPDATE, msg);
    }
    @ExceptionHandler(value = ObjectVersionException.class)
    public ResultWrapper<Void> objectVersionMismatch(ObjectVersionException e){
        Throwable cause = e.getCause();
        if (cause instanceof ResultException){
            return ResultWrapper.error((ResultException)cause);
        }
        String msg = e.getMessage();
        return ResultWrapper.error(Errors.OBJECT_VERSION_MISMATCH, msg);
    }
    @ExceptionHandler(value = NoObjectException.class)
    public ResultWrapper<Void> objectNotExist(NoObjectException e){
        Throwable cause = e.getCause();
        if (cause instanceof ResultException){
            return ResultWrapper.error((ResultException)cause);
        }
        String msg = e.getMessage();
        return ResultWrapper.error(Errors.NO_OBJECT, msg);
    }
    @ExceptionHandler(value = NoSuchElementException.class)
    public ResultWrapper<Void> noObject(NoSuchElementException e){
        String msg = e.getMessage();
        return ResultWrapper.error(Errors.NO_OBJECT, msg);
    }

    @ExceptionHandler(value = AddObjectException.class)
    public ResultWrapper<Void> addObject(AddObjectException e){
        Throwable cause = e.getCause();
        if (cause instanceof ResultException){
            return ResultWrapper.error((ResultException)cause);
        }
        String msg = e.getMessage();
        return ResultWrapper.error(Errors.OBJECT_SAVE, msg);
    }

    @ExceptionHandler(value = ResultException.class)
    public ResultWrapper<Void> commonHandle(ResultException e){
        return ResultWrapper.error(e);
    }

    private ResultException trackResultException(Throwable throwable){
        // 追溯系统支持的通用异常
        if (throwable instanceof ResultException){
            return (ResultException)throwable;
        }
        Throwable cause = throwable.getCause();
        if (cause == null){
            return null;
        }
        return trackResultException(cause);
    }
    private void setError(int error){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            attributes.setAttribute("java.cloud_response_error", error, RequestAttributes.SCOPE_REQUEST);
        }
    }

    @ExceptionHandler(value = Exception.class)
    public ResultWrapper<Void> advice(Exception e){
        ResultException resultException = trackResultException(e);
        if (resultException != null){
            return ResultWrapper.error(resultException);
        }
        Throwable throwable = e.getCause();
        String exceptionClass = (throwable == null ? e.getClass().getCanonicalName() : throwable.getClass().getCanonicalName());
        if (_errors.containsKey(exceptionClass)){
            return _errors.get(exceptionClass);
        }
        if (RECORD_EXISTS.contains(exceptionClass)){
            setError(adviceProperties.getObjectAlreadyExist());
            return ResultWrapper.error(adviceProperties.getObjectAlreadyExist(), "对象已经存在");
        }
        if (RECORD_PG_EXIST_EXP.equalsIgnoreCase(exceptionClass)){
            // 这是pg的异常
            SQLException sqlException = (SQLException)e;
            String sqlState = sqlException.getSQLState();
            if (RECORD_PG_EXIST_STATE.equals(sqlState)) {
                setError(adviceProperties.getObjectAlreadyExist());
                return ResultWrapper.error(adviceProperties.getObjectAlreadyExist(), "对象已经存在");
            }
//            try {
//                String sqlState = Reflect.on(throwable == null ? e : throwable).get("SQLState");
//                if (RECORD_PG_EXIST_STATE.equals(sqlState)) {
//                    setError(adviceProperties.getObjectAlreadyExist());
//                    return ResultWrapper.error(adviceProperties.getObjectAlreadyExist(), "对象已经存在");
//                }
//            }catch (Exception e1){
//                log.trace("解析pg异常的SQLState抛出异常：", e1);
//            }
        }

        if (exceptionResultProcessor != null) {
            ResultWrapper<Void> result = exceptionResultProcessor.exception(e);
            if (null != result) {
                return result;
            }
        }
        // 配置异常
        if (adviceProperties.getException() != null) {
            ExceptionCodeConfig config = adviceProperties.getException().get(e.getClass().getCanonicalName());
            if (null != config){
                return ResultWrapper.error(config.getCode(), config.getMessage());
            }
        }

        log.error("未知异常堆栈：", e);
        String msg = e.getMessage();
        return ResultWrapper.error(Code.UNKNOWN, StringUtils.isBlank(msg) ? "未知错误" : msg);
    }
}
